﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="BundleConfig.cs" company="ecblog.codeplex.com">
//   emrecoskun.net - ibrahimatay.com
// </copyright>
// <summary>
//   The bundle config.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace EcBlog
{
    using System;
    using System.IO;
    using System.Text;
    using System.Web;
    using System.Web.Hosting;
    using System.Web.Optimization;

    using dotless.Core;
    using dotless.Core.Abstractions;
    using dotless.Core.Importers;
    using dotless.Core.Input;
    using dotless.Core.Loggers;
    using dotless.Core.Parser;

    /// <summary>
    ///   The bundle config.
    /// </summary>
    public class BundleConfig
    {
        // For more information on Bundling, visit http://go.microsoft.com/fwlink/?LinkId=254725

        /// <summary>
        /// The register bundles.
        /// </summary>
        /// <param name="bundles">
        /// The bundles. 
        /// </param>
        public static void RegisterBundles(BundleCollection bundles)
        {
            bundles.Add(
                new ScriptBundle("~/bundles/jquery").Include("~/Scripts/jquery-{version}.js").Include(
                    "~/Scripts/jquery-ui-{version}.js").Include(
                        "~/Scripts/jquery.unobtrusive*", "~/Scripts/jquery.validate*"));
            bundles.Add(new ScriptBundle("~/bundles/modernizr").Include("~/Scripts/modernizr-*"));
            bundles.Add(new ScriptBundle("~/bundles/prettify").Include("~/Scripts/prettify/prettify.js"));
            bundles.Add(
                new ScriptBundle("~/bundles/adapt").Include("~/Scripts/adapt.config.js").Include("~/Scripts/adapt.js"));

            bundles.Add(new ScriptBundle("~/bundles/ecblog").Include("~/Scripts/tools.js"));

            bundles.Add(
                new StyleBundle("~/Content/themes/install").IncludeDirectory("~/Content/themes/install", "*.css"));
            bundles.Add(
                new StyleBundle("~/Content/themes/base/css").Include(
                    "~/Content/themes/base/jquery.ui.core.css",
                    "~/Content/themes/base/jquery.ui.resizable.css",
                    "~/Content/themes/base/jquery.ui.selectable.css",
                    "~/Content/themes/base/jquery.ui.accordion.css",
                    "~/Content/themes/base/jquery.ui.autocomplete.css",
                    "~/Content/themes/base/jquery.ui.button.css",
                    "~/Content/themes/base/jquery.ui.dialog.css",
                    "~/Content/themes/base/jquery.ui.slider.css",
                    "~/Content/themes/base/jquery.ui.tabs.css",
                    "~/Content/themes/base/jquery.ui.datepicker.css",
                    "~/Content/themes/base/jquery.ui.progressbar.css",
                    "~/Content/themes/base/jquery.ui.theme.css",
                    "~/Content/themes/base/widget.css"));
            bundles.Add(new StyleBundle("~/Content/prettify").Include("~/Content/prettify/prettify.css"));
            bundles.Add(
                new StyleBundle("~/Content/adapt").Include("~/Content/adapt/master.css").Include(
                    "~/Content/adapt/reset.css").Include("~/Content/adapt/text.css"));

            bundles.Add(new ScriptBundle("~/bundles/metro")
                .Include("~/Scripts/metro/jquery.metro.js")
                .Include("~/Scripts/metro/less.min.js")
                .Include("~/Scripts/metro/tilt.js"));
            var lessBundle = new Bundle("~/Content/themes/metro").Include(
                "~/Content/themes/metro/css", "~/Content/themes/metro/css/metroblog.less");
            lessBundle.Transforms.Add(new LessTransform());
            lessBundle.Transforms.Add(new CssMinify());
            bundles.Add(lessBundle);
//#if !DEBUG
//            BundleTable.EnableOptimizations = true;
//#endif
        }

        public class LessTransform : IBundleTransform
        {
            public void Process(BundleContext context, BundleResponse bundle)
            {
                if (bundle == null)
                {
                    throw new ArgumentNullException("bundle");
                }

                context.HttpContext.Response.Cache.SetLastModifiedFromFileDependencies();

                var lessParser = new Parser();
                ILessEngine lessEngine = this.CreateLessEngine(lessParser);

                var content = new StringBuilder(bundle.Content.Length);

                foreach (FileInfo file in bundle.Files)
                {
                    this.SetCurrentFilePath(lessParser, file.FullName);
                    string source = File.ReadAllText(file.FullName);
                    content.Append(lessEngine.TransformToCss(source, file.FullName));
                    content.AppendLine();

                    this.AddFileDependencies(lessParser);
                }

                bundle.ContentType = "text/css";
                bundle.Content = content.ToString();
            }

            /// <summary>
            /// Creates an instance of LESS engine.
            /// </summary>
            /// <param name="lessParser">The LESS parser.</param>
            private ILessEngine CreateLessEngine(Parser lessParser)
            {
                var logger = new AspNetTraceLogger(LogLevel.Debug, new Http());
                return new LessEngine(lessParser, logger, true, false);
            }

            /// <summary>
            /// Adds imported files to the collection of files on which the current response is dependent.
            /// </summary>
            /// <param name="lessParser">The LESS parser.</param>
            private void AddFileDependencies(Parser lessParser)
            {
                IPathResolver pathResolver = this.GetPathResolver(lessParser);

                foreach (string importedFilePath in lessParser.Importer.Imports)
                {
                    string fullPath = pathResolver.GetFullPath(importedFilePath);
                    HttpContext.Current.Response.AddFileDependency(fullPath);
                }

                lessParser.Importer.Imports.Clear();
            }

            /// <summary>
            /// Returns an <see cref="IPathResolver"/> instance used by the specified LESS lessParser.
            /// </summary>
            /// <param name="lessParser">The LESS prser.</param>
            private IPathResolver GetPathResolver(Parser lessParser)
            {
                var importer = lessParser.Importer as Importer;
                if (importer != null)
                {
                    var fileReader = importer.FileReader as FileReader;
                    if (fileReader != null)
                    {
                        return fileReader.PathResolver;
                    }
                }

                return null;
            }

            /// <summary>
            /// Informs the LESS parser about the path to the currently processed file.
            /// This is done by using custom <see cref="IPathResolver"/> implementation.
            /// </summary>
            /// <param name="lessParser">The LESS parser.</param>
            /// <param name="currentFilePath">The path to the currently processed file.</param>
            private void SetCurrentFilePath(Parser lessParser, string currentFilePath)
            {
                var importer = lessParser.Importer as Importer;
                if (importer != null)
                {
                    var fileReader = importer.FileReader as FileReader;

                    if (fileReader == null)
                    {
                        importer.FileReader = fileReader = new FileReader();
                    }

                    var pathResolver = fileReader.PathResolver as ImportedFilePathResolver;

                    if (pathResolver != null)
                    {
                        pathResolver.CurrentFilePath = currentFilePath;
                    }
                    else
                    {
                        fileReader.PathResolver = new ImportedFilePathResolver(currentFilePath);
                    }
                }
                else
                {
                    throw new InvalidOperationException("Unexpected importer type on dotless parser");
                }
            }
        }
    }

    public class ImportedFilePathResolver : IPathResolver
    {
        private string currentFileDirectory;

        private string currentFilePath;

        public ImportedFilePathResolver(string currentFilePath)
        {
            this.CurrentFilePath = currentFilePath;
        }

        /// <summary>
        /// Gets or sets the path to the currently processed file.
        /// </summary>
        public string CurrentFilePath
        {
            get
            {
                return this.currentFilePath;
            }
            set
            {
                this.currentFilePath = value;
                this.currentFileDirectory = Path.GetDirectoryName(value);
            }
        }

        /// <summary>
        /// Returns the absolute path for the specified improted file path.
        /// </summary>
        /// <param name="filePath">The imported file path.</param>
        public string GetFullPath(string filePath)
        {
            filePath = filePath.Replace('\\', '/').Trim();

            if (filePath.StartsWith("~"))
            {
                filePath = VirtualPathUtility.ToAbsolute(filePath);
            }

            if (filePath.StartsWith("/"))
            {
                filePath = HostingEnvironment.MapPath(filePath);
            }
            else if (!Path.IsPathRooted(filePath))
            {
                filePath = Path.Combine(this.currentFileDirectory, filePath);
            }

            return filePath;
        }
    }
}